import VectorSource from 'ol/source/Vector'
import Feature from 'ol/Feature'
import LineString from 'ol/geom/LineString'
import { Style, Stroke } from 'ol/style'
import { fromLonLat } from 'ol/proj'

export type FlowLineSpeedType = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'

function getEasingFunc(type: FlowLineSpeedType) {
  switch (type) {
    case 'easeIn':
      return (t: number) => t * t
    case 'easeOut':
      return (t: number) => t * (2 - t)
    case 'easeInOut':
      return (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t
    case 'linear':
    default:
      return (t: number) => t
  }
}

export interface FlowLineOptions {
  flowSpeed?: number // 动画速度
  flowLength?: number // 飞线段长度比例
  style?: Style[] // 飞线样式
  speedType?: FlowLineSpeedType // 速度类型
  count?: number // 飞线条数，默认1
  multi?: boolean // 是否多条飞线，默认false
}

export class FlowLine {
  private vectorSource: VectorSource
  private animationFrameId: number | null = null
  private flowFeatures: Feature[] = []
  private pathCoords: number[][] = []
  private options: Required<FlowLineOptions>
  private startTime: number = 0
  private running: boolean = false
  private easing: (t: number) => number

  constructor(vectorSource: VectorSource, geojson: any, options: FlowLineOptions = {}) {
    this.vectorSource = vectorSource
    this.options = {
      flowSpeed: options.flowSpeed ?? 0.0002,
      flowLength: options.flowLength ?? 0.15,
      style: options.style ?? [
        new Style({
          stroke: new Stroke({
            color: 'rgba(0, 234, 255, 0.4)',
            width: 18,
            lineCap: 'round',
            lineJoin: 'round'
          })
        }),
        new Style({
          stroke: new Stroke({
            color: 'rgba(0, 234, 255, 0.9)',
            width: 6,
            lineCap: 'round',
            lineJoin: 'round'
          })
        })
      ],
      speedType: options.speedType ?? 'linear',
      count: options.count ?? 1,
      multi: options.multi ?? false
    }
    this.easing = getEasingFunc(this.options.speedType)
    this.initPath(geojson)
  }

  private initPath(geojson: any) {
    if (!geojson?.features?.length) return
    this.pathCoords = geojson.features[0].geometry.coordinates
    this.vectorSource.clear()
    this.flowFeatures = []
    const count = this.options.multi ? this.options.count : 1
    for (let i = 0; i < count; i++) {
      const feature = new Feature()
      feature.setStyle(this.options.style)
      this.vectorSource.addFeature(feature)
      this.flowFeatures.push(feature)
    }
  }

  private updateFlowAnimation = () => {
    if (!this.flowFeatures.length || this.pathCoords.length < 2) return
    const line = new LineString(this.pathCoords.map(c => fromLonLat(c)))
    const totalLength = line.getLength()
    const now = Date.now()
    if (!this.startTime) this.startTime = now
    let t = ((now - this.startTime) * this.options.flowSpeed) % 1
    t = this.easing(t)
    const count = this.options.multi ? this.options.count : 1
    for (let i = 0; i < count; i++) {
      // 多条飞线时，每条飞线有不同的起点偏移
      let offset = this.options.multi ? (i / count) : 0
      let tOffset = (t + offset) % 1
      const startFrac = tOffset
      const endFrac = Math.min(tOffset + this.options.flowLength, 1)
      const startLen = totalLength * startFrac
      const endLen = totalLength * endFrac
      const coords: number[][] = []
      let accLen = 0
      const allCoords = line.getCoordinates()
      for (let j = 0; j < allCoords.length - 1; j++) {
        const segStart = allCoords[j]
        const segEnd = allCoords[j + 1]
        const segLen = new LineString([segStart, segEnd]).getLength()
        if (accLen + segLen < startLen) {
          accLen += segLen
          continue
        }
        if (coords.length === 0) {
          const ratio = (startLen - accLen) / segLen
          coords.push([
            segStart[0] + (segEnd[0] - segStart[0]) * ratio,
            segStart[1] + (segEnd[1] - segStart[1]) * ratio
          ])
        }
        if (accLen + segLen > endLen) {
          const ratio = (endLen - accLen) / segLen
          coords.push([
            segStart[0] + (segEnd[0] - segStart[0]) * ratio,
            segStart[1] + (segEnd[1] - segStart[1]) * ratio
          ])
          break
        } else {
          coords.push(segEnd)
        }
        accLen += segLen
      }
      if (coords.length >= 2) {
        this.flowFeatures[i].setGeometry(new LineString(coords))
      }
    }
    if (this.running) {
      this.animationFrameId = requestAnimationFrame(this.updateFlowAnimation)
    }
  }

  public start() {
    this.running = true
    this.startTime = Date.now()
    this.animationFrameId = requestAnimationFrame(this.updateFlowAnimation)
  }

  public stop() {
    this.running = false
    if (this.animationFrameId) {
      cancelAnimationFrame(this.animationFrameId)
      this.animationFrameId = null
    }
  }

  public destroy() {
    this.stop()
    this.vectorSource.clear()
  }
} 